﻿using System;
using System.Reflection.Emit;
using msilgc.cbuilder.CSyntaxTree;
using msilgc.cbuilder.MSILSyntaxTree;
using msilgc.CSyntaxTree;

namespace msilgc.cbuilder.CBuilder
{
    internal class LoadArrayElementTranslator : ITranslator {
        private readonly VariableFactory _varFactory;

        public LoadArrayElementTranslator(VariableFactory varFactory) {
            _varFactory = varFactory;
        }

        private static Type InferTargetType(Instruction inst) {
            if(inst.Inst == OpCodes.Ldelem_I) {
                return typeof(int);
            }
            if(inst.Inst == OpCodes.Ldelem_I1) {
                return typeof(int);
            }
            if(inst.Inst == OpCodes.Ldelem_I2) {
                return typeof(int);
            }
            if(inst.Inst == OpCodes.Ldelem_I4) {
                return typeof(int);
            }
            if(inst.Inst == OpCodes.Ldelem_I8) {
                return typeof(long);
            }
            if(inst.Inst == OpCodes.Ldelem_R4) {
                return typeof(float);
            }
            if(inst.Inst == OpCodes.Ldelem_R8) {
                return typeof(double);
            }
            if(inst.Inst == OpCodes.Ldelem_U1) {
                return typeof(uint);
            }
            if(inst.Inst == OpCodes.Ldelem_U2) {
                return typeof(uint);
            }
            if(inst.Inst == OpCodes.Ldelem_U4) {
                return typeof(uint);
            }
            throw new Exception("unknown instruction: " + inst);
        }

        public ICStatement TranslateAndAdd(CMethodBody methodBody, CBasicBlock currentBlock, BasicBlock block, Instruction instruction) {
            IVariable target = _varFactory.CreateNext(InferTargetType(instruction));
            IVariable indexVar = EvaluationStackHelper.PopVariableOffEvaluationStack(currentBlock);
            IVariable arrayRefVar = EvaluationStackHelper.PopVariableOffEvaluationStack(currentBlock);
            AssignToLocalVariableFromArray arrayStm = new AssignToLocalVariableFromArray(target, arrayRefVar, indexVar);
            currentBlock.Statements.Add(arrayStm);
            return arrayStm;
        }
    }
}